package zip_util

import (
	"archive/zip"
	"io"
	"os"
	"path/filepath"
	"strings"
)

// 压缩文件或文件夹。
// del为true则在压缩成功后删除原文件或文件夹
func Zip(src string, dstZip string, del bool) error {
	zipfile, err := os.Create(dstZip)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	archive := zip.NewWriter(zipfile)
	defer archive.Close()

	err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		header.Name = strings.TrimPrefix(path, filepath.Dir(src)+"/")
		if info.IsDir() {
			header.Name += "/"
		} else {
			header.Method = zip.Deflate
		}

		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}

		if !info.IsDir() {
			file, err := os.Open(path)
			if err != nil {
				return err
			}
			defer file.Close()
			_, err = io.Copy(writer, file)
			if err != nil {
				return err
			}
		}
		return nil
	})

	if err == nil && del {
		// 压缩成功后删除源文件或源文件夹
		err = os.RemoveAll(src)
	}

	return err
}

// 解压至目标文件夹。
// del为true则在解压成功后删除zip文件。
func Unzip(zipFile string, destDir string, del bool) error {
	zipReader, err := zip.OpenReader(zipFile)
	if err != nil {
		return err
	}
	defer zipReader.Close()

	for _, f := range zipReader.File {
		fpath := filepath.Join(destDir, f.Name)
		if f.FileInfo().IsDir() {
			// 创建目标文件夹
			os.MkdirAll(fpath, os.ModePerm)
		} else {
			if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
				return err
			}
			// 打开zip文件
			inFile, err := f.Open() // inFile不能在循环外创建，必须在循环内创建，否则defer调用Close时可能会关闭错对象。
			if err != nil {
				return err
			}
			defer inFile.Close()
			// 打开目标文件
			outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer outFile.Close()
			// 解压文件
			_, err = io.Copy(outFile, inFile)
			if err != nil {
				return err
			}
		}
	}

	if del {
		return os.Remove(zipFile)
	}
	return nil
}
